﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Krilib.Control.Surface;
using System.Drawing;

namespace Krile.Forms.MainForm.Controls.Surface
{
    internal class SurfaceMouseManager
    {
        SurfaceCore parent;
        SurfaceRenderer renderer;
        IMouseControllable lastPointed;
        string lastTooltipText;
        ToolTip toolTip;

        internal SurfaceMouseManager(SurfaceCore parent, SurfaceRenderer renderer)
        {
            this.parent = parent;
            this.renderer = renderer;
            //initialize
            lastPointed = null;
            lastTooltipText = null;
            toolTip = new ToolTip();
            toolTip.ShowAlways = true;
            toolTip.Active = false;

            //events set
            parent.MouseLeave += new EventHandler(parent_MouseLeave);
            parent.MouseMove += new MouseEventHandler(parent_MouseMove);
            parent.MouseClick += new MouseEventHandler(parent_MouseClick);
            parent.MouseDoubleClick += new MouseEventHandler(parent_MouseDoubleClick);
            parent.MouseUp += new MouseEventHandler(parent_MouseUp);
            parent.MouseDown += new MouseEventHandler(parent_MouseDown);
            parent.MouseWheel += new MouseEventHandler(parent_MouseWheel);
            parent.OnSelectedItemChanged += new Action(parent_OnSelectedItemChanged);
        }

        void parent_MouseWheel(object sender, MouseEventArgs e)
        {
            //↑ +
            //↓ -
            long nval = parent.VerticalPosition - (e.Delta / Math.Abs(e.Delta)) * Core.Config.Appearance.Renderer.Detail.NormalListHeight;
            if (nval < 0)
                nval = 0;
            parent.VerticalPosition = nval;
        }

        void parent_MouseDown(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseDown(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseUp(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseUp(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseClick(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseClick(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                tmci.OnMouseDoubleClick(sender, xe);
                if (!xe.IsThrowedSub)
                    break;
                xe.SetTerminate();
            }
        }

        void parent_MouseMove(object sender, MouseEventArgs e)
        {
            ChainMouseEventArgs xe = ChainMouseEventArgs.Factory(e);
            foreach (var tmci in GetItemByPoint(e.Location))
            {
                //チップヘルプ処理
                if (!tmci.Equals(lastPointed) || tmci.HelptipText != lastTooltipText)
                {
                    //以前指していたオブジェクトと違うか、またはヘルプテキストが変更されていたら再セット
                    releaseLastPointed();
                    lastPointed = tmci;
                    if (tmci == null)
                        return;
                    if (!String.IsNullOrEmpty(tmci.HelptipText))
                    {
                        toolTip.SetToolTip(parent, tmci.HelptipText);
                        toolTip.Active = true;
                    }
                    lastTooltipText = tmci.HelptipText;

                    tmci.OnMouseEnter(sender, xe);
                    return;
                }
                //イベント呼び出し
                tmci.OnMouseMove(sender, xe);
                return;
            }
            releaseLastPointed();
        }

        void parent_MouseLeave(object sender, EventArgs e)
        {
            releaseLastPointed();
        }

        object itemsLocker = new object();
        IMouseControllable[] items = null;
        void parent_OnSelectedItemChanged()
        {
            lock (itemsLocker)
            {
                if (parent.SelectedStatus == null)
                    items = null;
                else
                    items = parent.SelectedStatus.BaseStatus.text.MessageObjects;
            }
        }

        /// <summary>
        /// 座標に衝突しているアイテムを検索し返します。
        /// </summary>
        /// <param name="location">対象座標</param>
        /// <returns>衝突判定されたアイテム</returns>
        IEnumerable<IMouseControllable> GetItemByPoint(Point location)
        {
            lock (itemsLocker)
                if (items != null)
                    foreach (var i in items)
                        if (i.HitTest(location))
                            yield return i;
            foreach (var ri in renderer.GetMouseControllables())
                if (ri.HitTest(location))
                    yield return ri;
        }

        /// <summary>
        /// 以前アクティブだったステータスのリソースを解放します。
        /// </summary>
        void releaseLastPointed()
        {
            if (lastPointed != null)
            {
                lastPointed.OnMouseLeave(parent, new ChainMouseEventArgs());
                lastPointed = null;
                toolTip.Active = false;
            }
        }

        public void Dispose()
        {
            toolTip.Dispose();
        }
    }
}
